#include <QLibrary>
#include <QDir>

#include "datasource.h"
#include "globals.h"

#include "sources/owen.h"
#include "sources/microfor.h"

FDataSourceFactory* dataSourceFactory;

class FPluginDSConstructor;
typedef FPluginDSConstructor * (__stdcall *TCreateInstance)();

template<class T>
class FDataSourceCustomConstructor: public FDataSourceConstructor
{
public:
    FDataSourceCustomConstructor(QString name):FDataSourceConstructor(name){}
    FDataSource *create(QVariant params){return new T(params);}
};

class FPluginSource
{
public:
    enum FEStatus
    {
        Good=0,
        Bad
    };
    virtual double __stdcall readValue()=0;
    virtual int __stdcall status()=0;
};

class FPluginDSConstructor
{
public:    
    virtual const char * __stdcall name()=0;
    virtual FPluginSource * __stdcall create(const char* parameter)=0;
};

class FDataSourcePlugin : public FDataSource
{
private:
    FPluginSource *src;
public:
    FDataSourcePlugin(FPluginSource *source=NULL):FDataSource(QVariant()),src(source){}
    virtual ~FDataSourcePlugin(){if (src) delete src;}
    virtual CValue readValue()
    {
        if (!src) return CValue();
        double value = src->readValue();
        if (src->status()!=FPluginSource::Good) return CValue();
        return CValue(value);
    }
};


void to_1251(QString s,char *ascii,unsigned int size)
{
    QTextCodec *codec=QTextCodec::codecForName("Windows-1251");
    QByteArray ba=codec->fromUnicode(s);
    if((unsigned int)ba.size()>=size) return;
    memcpy(ascii,ba.data(),ba.size());
    ascii[ba.size()]=0;
}

FDataSourceFactory::~FDataSourceFactory()
{
    QHash<QString, QLibrary* > ::iterator i = loadedLibraries.begin();

    for(;i!=loadedLibraries.end(); i++)
    {
        QLibrary* lib = i.value();
        if (!lib) continue;
        if (!lib->unload())
        {
            qDebug() << "Cannot unload lib: " << i.key();
        };
        delete lib;
    };
}

class FDataSourceCreatorPlugin: public FDataSourceConstructor
{
    FPluginDSConstructor *dsc;
public:
    FDataSourceCreatorPlugin(QString name=QString(),FPluginDSConstructor * ds=NULL):
        FDataSourceConstructor(name),
        dsc(ds){}
    virtual FDataSource* create(QVariant param)
    {
        if (!dsc) return NULL;
        QString t = param.toString();
        char mass[10000];
        to_1251(t,mass,10000);
        return new FDataSourcePlugin(dsc->create(mass));
    }
    virtual ~FDataSourceCreatorPlugin()
    {
        delete dsc;
    }
};

FDataSourceFactory::FDataSourceFactory()
{
   addConstructor(new FDataSourceCustomConstructor<COwenSource>("owen"));
   addConstructor(new FDataSourceCustomConstructor<CMicroforHumiditySource>("microfor-humidity"));
   addConstructor(new FDataSourceCustomConstructor<CMicroforTemperatureSource>("microfor-temperature"));

   QDir dir("./plugins");
   QStringList flt;
   flt << "*.dll";
   dir.setNameFilters(flt);

   QFileInfoList plugins = dir.entryInfoList();

   foreach(QFileInfo plugin,plugins)
   {
       QString filePath = plugin.absoluteFilePath();
       qDebug() << "SourcePlugin: filename=" << filePath;
       QLibrary *lib = new QLibrary(filePath);
       if (!lib->load())
       {
           qDebug() << "SourcePlugin: load error.";
           delete lib;
           continue;
       }
       TCreateInstance createInstance = (TCreateInstance) lib->resolve("CreateInstance");
       if (!createInstance)
       {
           qDebug() << "SourcePlugin: resolve \"CreateInstance\" error.";
           lib->unload();
           delete lib;
           continue;
       }
       FPluginDSConstructor *ds = createInstance();

       if (!ds)
       {
           qDebug() << "SourcePlugin: create instance \"FPluginConstructor\" failed";
           lib->unload();
           delete lib;
           continue;
       }
       QString name = QString::fromUtf8(ds->name());

       qDebug() << "success loading plugin: " << name;

       //loadedLibraries << filePath;
       loadedLibraries[filePath] = lib;
       addConstructor(new FDataSourceCreatorPlugin(name,ds));
   }
}
